package com.pig4cloud.pig.api.util;

import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.imageio.ImageIO;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.MultimediaInfo;

public class UploadUtils {

    private static Logger logger = LoggerFactory.getLogger(UploadUtils.class);
    // 图片的后缀
    public static final String[] IMAGE_EXT = new String[] {"jpg", "jpeg", "gif", "png", "bmp"};
    // 图片的后缀
    public static final String[] VIDEO_EXT = new String[] {"mp4", "mpeg", "3gp", "avi"};
    // 视频固定位置截取封面(%) 例：5%写0.05,20%写0.2
    public static final Double[] VIDEO_RATE_EXT = new Double[] {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};

    /**
     * 上传文件 webx使用
     * 
     * @param fileItem:文件
     * @param dir:上传路径
     * @param size:文件大小限制
     * @return
     */
    public static Map<String, Object> uploadImg(FileItem fileItem, String dir, long size) {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        if (fileItem != null) {
            if (size != 0 && size < fileItem.getSize()) {
                resultMap.put("code", 1);
                resultMap.put("status", Constants.RESULT_STATUS_ERROR);
                resultMap.put("message", "上传文件不可超过" + (size / 1024) + "KB");
                logger.error("上传文件过大...上传文件不可超过" + (size / 1024) + "KB");
                return resultMap;
            }
            if (StringUtils.isEmpty(dir)) {
                resultMap.put("code", 1);
                resultMap.put("status", Constants.RESULT_STATUS_ERROR);
                resultMap.put("msg", "文件上传失败");
                logger.error("未找到文件目录...请检查配置文件");
            } else {
                try {
                    // 获取后缀名
                    String suffix = fileItem.getName().substring(fileItem.getName().lastIndexOf("."));
                    // 生成上传到服务器上的文件名 使用MD5对当前上传时间进行加密的方式
                    String now = System.currentTimeMillis() + "";
                    String fileName = now.substring(0, 13) + suffix;
                    // 将文件写到指定目录
                    logger.info("写入文件" + fileName);
                    File file = new File(dir + "/" + fileName);
                    file.setReadable(true);
                    fileItem.write(file);
                    // TODO linux 设置文件读取权限 部署时要将本行注释释放
                    if (OsUtil.isLinux()) {
                        Runtime.getRuntime().exec("chmod 755 " + dir + "/" + fileName);
                    }
                    resultMap.put("file", fileName);
                    logger.info("文件上传成功");
                    resultMap.put("code", 0);
                    resultMap.put("status", Constants.RESULT_STATUS_SUCCESS);
                    resultMap.put("msg", Constants.SUCCESS_MSG_UPLOAD);
                } catch (Exception e) {
                    resultMap.put("code", 1);
                    resultMap.put("status", Constants.RESULT_STATUS_ERROR);
                    resultMap.put("msg", "文件上传失败");
                    logger.error("文件上传失败...", e);
                }
            }
        } else {
            resultMap.put("code", 1);
            resultMap.put("status", Constants.RESULT_STATUS_ERROR);
            resultMap.put("msg", "请选择文件");
        }
        return resultMap;
    }

    /**
     * 图片上传
     * 
     * @param realPath
     * @param imgFile
     * @return
     */
    public static Map<String, Object> imageUpload(String realPath, MultipartFile imgFile, long size) {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        if (imgFile != null) {
            if (size != 0 && size < imgFile.getSize()) {
                resultMap.put("code", 1);
                resultMap.put("status", Constants.RESULT_STATUS_ERROR);
                resultMap.put("msg", "上传文件不可超过" + (size / 1024) + "KB");
                logger.error("上传文件过大...上传文件不可超过" + (size / 1024) + "KB");
                return resultMap;
            }
            InputStream in = null;
            FileOutputStream out = null;
            try {
                // 获取图片名称
                String filename = imgFile.getOriginalFilename();
                // 获取后缀名
                String suffix = filename.substring(filename.lastIndexOf(".") + 1);
                // 判断是否是图片
                if (!getIsImg(suffix)) {
                    resultMap.put("code", 1);
                    resultMap.put("status", Constants.RESULT_STATUS_ERROR);
                    resultMap.put("msg", "上传文件格式不正确");
                    logger.error("上传文件格式不正确");
                    return resultMap;
                }
                // 判断是否存在文件夹，不存在则新建
                File fileMkdirs = new File(realPath);
                if (!fileMkdirs.exists() && !fileMkdirs.isDirectory()) {
                    fileMkdirs.mkdirs();
                }
                // 生成上传到服务器上的文件名 使用MD5对当前上传时间进行加密的方式
                String now = System.currentTimeMillis() + "";
                String realName = Coder.encodeMd5(now).substring(0, 13) + "." + suffix;

                // 文件新名称,这个方式的名称可能存在非法字符，导致图片文件同步失败
                // String realName = System.currentTimeMillis()+"_"+ imgFile.getOriginalFilename();
                in = imgFile.getInputStream();
                out = new FileOutputStream(realPath + File.separator + realName);
                int length = 0;
                byte[] b = new byte[1024];
                while ((length = in.read(b)) != -1) {
                    out.write(b, 0, length);
                }
                // TODO linux 设置文件读取权限 部署时要将本行注释释放
                if (OsUtil.isLinux()) {
                    Runtime.getRuntime().exec("chmod 644 " + realPath + File.separator + realName);
                }

                /**
                 * 获取图片的宽高
                 */
                BufferedImage sourceImg = ImageIO.read(new FileInputStream(realPath + File.separator + realName));
                int width = sourceImg.getWidth();
				int height = sourceImg.getHeight();
                resultMap.put("width", width);
                resultMap.put("height", height);
                resultMap.put("filename", realName);
                if (height < width) {
                    resultMap.put("screenType", 1);// 横屏
                } else {
                    resultMap.put("screenType", 2);// 竖屏
                }
                resultMap.put("format", suffix);// 图片格式
                String md5 = DigestUtils.md5Hex(new FileInputStream(realPath + File.separator + realName));
                resultMap.put("md5", md5);
                resultMap.put("realPath", realPath + realName);
				resultMap.put("size", imgFile.getSize());// 大小

                logger.info("文件上传成功");
                resultMap.put("code", 0);
                resultMap.put("status", Constants.RESULT_STATUS_SUCCESS);
                resultMap.put("msg", Constants.SUCCESS_MSG_UPLOAD);
            } catch (Exception e) {
                e.printStackTrace();
                resultMap.put("code", 1);
                resultMap.put("status", Constants.RESULT_STATUS_ERROR);
                resultMap.put("msg", "文件上传失败");
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            resultMap.put("code", 1);
            resultMap.put("status", Constants.RESULT_STATUS_ERROR);
            resultMap.put("msg", "请选择文件");
        }
        return resultMap;
    }

    /**
     * 视频上传
     * 
     * @param realPath
     * @param file
     * @return
     */
    public static Map<String, Object> videoUpload(MultipartFile file, String realPath, long size) {
		/**
		 * 上传路径本地测试用
		 * realPath = "E:/nginx-1.15.0/html/pig-ui/upload/video";
		 */
		Map<String, Object> resultMap = new HashMap<>();
        if (file != null) {
            if (size != 0 && size < file.getSize()) {
                resultMap.put("code", 1);
                resultMap.put("msg", "上传文件不可超过" + (size / 1024 / 1024) + "M");
                logger.error("上传文件过大...上传文件不可超过" + (size / 1024 / 1024) + "M");
                return resultMap;
            }
            InputStream in = null;
            FileOutputStream out = null;
            try {
                // 获取名称
                String filename = file.getOriginalFilename();
                // 获取后缀名
				String suffix= "";
				if(StringUtils.isNotBlank(filename)) {
					suffix = filename.substring(filename.lastIndexOf(".") + 1);
				}
                // 判断是否是视频
                if (!getIsVideo(suffix)) {
                    resultMap.put("code", 1);
                    resultMap.put("msg", "上传文件格式不正确");
                    logger.error("上传文件格式不正确");
                    return resultMap;
                }
                // 判断是否存在文件夹，不存在则新建
                File fileMkdirs = new File(realPath);
                if (!fileMkdirs.exists() && !fileMkdirs.isDirectory()) {
                    fileMkdirs.mkdirs();
                }
                // 文件新名称
                UUID uid = UUID.randomUUID();
                String uuid = String.valueOf(uid);
                String realName = uuid + "." + suffix;
                in = file.getInputStream();
                out = new FileOutputStream(realPath + File.separator + realName);
                int length = 0;
                byte[] b = new byte[1024];
                while ((length = in.read(b)) != -1) {
                    out.write(b, 0, length);
                }
                if (OsUtil.isLinux()) {
                    Runtime.getRuntime().exec("chmod 644 " + realPath + File.separator + realName);
                }
                /**
                 * 截取视频封面图
                 */
                try {
                    List<Map<String, Object>> videoImgList = fetchFrameBatch(realPath + File.separator + realName, realPath, uuid);
                    resultMap.put("videoImgList", videoImgList);
                } catch (Exception e) {
                    e.printStackTrace();
                    resultMap.put("code", 1);
                    resultMap.put("msg", "获取视频截图异常");
                    logger.info(">>>上传视频，获取视频截图异常：{}", e.getMessage());
                    return resultMap;
                }
                /**
                 * 获取宽高等信息,视频帧宽高
                 */
                File source = new File(realPath + File.separator + realName);
                Encoder encoder = new Encoder();
                MultimediaInfo m = encoder.getInfo(source);
                long ls = m.getDuration();
                resultMap.put("duration", new BigDecimal(ls).divide(new BigDecimal(1000)).setScale(3,BigDecimal.ROUND_HALF_UP));// 视频时长
                resultMap.put("height", m.getVideo().getSize().getHeight());// 视频高度
                resultMap.put("width", m.getVideo().getSize().getWidth());// 视频宽度
                if (m.getVideo().getSize().getHeight() < m.getVideo().getSize().getWidth()) {
                    resultMap.put("screenType", 1);// 横屏
                } else {
                    resultMap.put("screenType", 2);// 竖屏
                }
                resultMap.put("format", suffix);// 视频格式
                resultMap.put("size", file.getSize());// 大小
                String md5 = DigestUtils.md5Hex(new FileInputStream(realPath + File.separator + realName));
                resultMap.put("md5", md5);
                resultMap.put("realPath", realPath + realName);

                resultMap.put("filename", realName);
                logger.info("文件上传成功");
                resultMap.put("code", 0);
                resultMap.put("msg", Constants.SUCCESS_MSG_UPLOAD);
            } catch (Exception e) {
                e.printStackTrace();
                resultMap.put("code", 1);
                resultMap.put("msg", "文件上传失败");
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            resultMap.put("code", 1);
            resultMap.put("msg", "请选择文件");
        }
        return resultMap;
    }

    /**
     * 文件上传
     *
     * @param file
     * @param realPath
	 * @param downUrl
	 * @param size
     */
    public static Map<String, Object> fileUpload(MultipartFile file, String realPath, String downUrl, long size) {
        Map<String, Object> resultMap = new HashMap<String, Object>();

        InputStream in = null;
        FileOutputStream out = null;
        try {
            if (file == null) {
                resultMap.put("code", 1);
                resultMap.put("msg", "请选择文件");
                return resultMap;
            }

            if (size != 0 && size < file.getSize()) {
                resultMap.put("code", 1);
                resultMap.put("msg", "上传文件不可超过" + (size / 1024 / 1024) + "M");
                logger.error("上传文件过大...上传文件不可超过" + (size / 1024 / 1024) + "M");
                return resultMap;
            }

            File absoluteDirFile = new File(realPath);
            if (!absoluteDirFile.exists()) {
                absoluteDirFile.mkdirs();
                // TODO linux 设置文件读取权限 部署时要将本行注释释放
                if (!OsUtil.isWindows()) {
                    Runtime.getRuntime().exec("chmod 755 -R " + absoluteDirFile.getPath());
                }
            }

            // 获取名称
            String filename = file.getOriginalFilename();
            // 获取后缀名
            String suffix = filename.substring(filename.lastIndexOf(".") + 1);

            // 文件新名称
            UUID uid = UUID.randomUUID();
            String uuid = String.valueOf(uid);
            String realName = uuid + "." + suffix;
            String filePathName = realPath + File.separator + realName;

            in = file.getInputStream();
            out = new FileOutputStream(filePathName);
            int length = 0;
            byte[] b = new byte[1024];
            while ((length = in.read(b)) != -1) {
                out.write(b, 0, length);
            }

            String md5 = DigestUtils.md5Hex(new FileInputStream(filePathName));

            resultMap.put("downUrl", downUrl + realName);
            resultMap.put("md5", md5);
            resultMap.put("realPath", realPath + realName);
            resultMap.put("filename", realName);

            logger.info("文件上传成功");
            resultMap.put("code", 0);
            resultMap.put("msg", Constants.SUCCESS_MSG_UPLOAD);
        } catch (Exception e) {
            e.printStackTrace();
            resultMap.put("code", 1);
            resultMap.put("msg", "文件上传失败");
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                logger.error("文件流操作异常", e);
            }
        }
        return resultMap;
    }

    /**
     * 截取视频封面图（多图）
     * 
     * @param videoFile
     * @param realPath
     * @throws Exception
     */
    public static List<Map<String, Object>> fetchFrameBatch(String videoFile, String realPath, String uuid) {
        List<Map<String, Object>> videoImgList = new ArrayList<>();
        try {
            FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoFile);
            ff.start();
            int lenght = ff.getLengthInFrames();
            int i = 0;
            Frame frame = null;
            while (i < lenght) {
                // 根据配置获取指定位置帧
                frame = ff.grabImage();// grabFrame();
                for (Double rate : VIDEO_RATE_EXT) {
                    int l = (int)(lenght * rate);
                    if ((i == l) && (null != frame)) {
                        String imageFile = realPath + uuid + "_" + (int)(rate * 100) + "_image.jpg";
                        File targetFile = new File(imageFile);
                        ImageIO.write(FrameToBufferedImage(frame), "jpg", targetFile);

                        Map<String, Object> videoImg = new HashMap<>();
                        videoImg.put("imageName", uuid + "_" + (int)(rate * 100) + "_image.jpg");
                        videoImg.put("videoRate", (int)(rate * 100));
                        videoImgList.add(videoImg);
                        break;
                    }
                }
                i++;
            }
            ff.close();
            ff.stop();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return videoImgList;
    }

    /**
     * 截取视频封面图
     * 
     * @param videoFile
     * @param imageFile
     * @throws Exception
     */
    public static void fetchFrame(String videoFile, String imageFile) {
        try {
			// 判断是否存在文件夹，不存在则新建
			File fileMkdirs = new File(imageFile);
			if (!fileMkdirs.exists() && !fileMkdirs.isDirectory()) {
				fileMkdirs.mkdirs();
			}
            File targetFile = new File(imageFile);
            FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoFile);
            ff.start();
            int lenght = ff.getLengthInFrames();
            int i = 0;
            Frame frame = null;
            while (i < lenght) {
                // 过滤前5帧，避免出现全黑的图片，依自己情况而定
                frame = ff.grabFrame();
                if ((i > 1) && (frame.image != null)) {
                    break;
                }
                i++;
            }
            ImageIO.write(FrameToBufferedImage(frame), "jpg", targetFile);
            ff.close();
            ff.stop();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

	/**
	 * 截取视频封面图
	 *
	 * @param videoFile
	 * @throws Exception
	 */
	public static InputStream fetchFrameToInputStream(String videoFile) {
		try {
			FFmpegFrameGrabber ff = new FFmpegFrameGrabber(videoFile);
			//FFmpegFrameGrabber ff = new FFmpegFrameGrabber("E:/D/我的文档/图片/小狗和小鸡.mp4");
			//FFmpegFrameGrabber ff = new FFmpegFrameGrabber("http://localhost:8821/admin/sys-file/readFile/admaterial/2ccfd4e3ee5e7a46ecf6850ee1c7ef60.mp4");
			//FFmpegFrameGrabber ff = new FFmpegFrameGrabber("http://test.pangu.3399.com/admin/sys-file/readFile/admaterial/fb967aeb685b75b8b7f27590520efa26.mp4");
			//FFmpegFrameGrabber ff = new FFmpegFrameGrabber("http://test.pangu.3399.com/admin/sys-file/readFile/admaterial/2ccfd4e3ee5e7a46ecf6850ee1c7ef60.mp4");//xiaogou
			//FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault("http://test.pangu.3399.com/admin/sys-file/readFile/admaterial/2ccfd4e3ee5e7a46ecf6850ee1c7ef60.mp4");
			//FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault("http://test.pangu.3399.com/upload/video/20211022/4ef95fa6-5d5b-4a0d-ad3e-3544564163b1.mp4");
			ff.start();
			int lenght = ff.getLengthInFrames();
			int i = 0;
			Frame frame = null;
			while (i < lenght) {
				// 过滤前5帧，避免出现全黑的图片，依自己情况而定
				frame = ff.grabFrame();
				if ((i > 1) && (null != frame) && (frame.image != null)) {
					break;
				}
				i++;
			}
			InputStream input = null;
			if (null != frame) {
				ByteArrayOutputStream os = new ByteArrayOutputStream();
				ImageIO.write(FrameToBufferedImage(frame), "jpg", os);
				input = new ByteArrayInputStream(os.toByteArray());
			}
			ff.close();
			ff.stop();
			return input;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 将BufferedImage转换为InputStream
	 * @param image
	 * @return
	 */
	public static InputStream bufferedImageToInputStream(RenderedImage image){
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		try {
			ImageIO.write(image, "jpg", os);
			InputStream input = new ByteArrayInputStream(os.toByteArray());
			return input;
		} catch (IOException e) {
			logger.error("提示:",e);
		}
		return null;
	}

    private static RenderedImage FrameToBufferedImage(Frame frame) {
        // 创建BufferedImage对象
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage bufferedImage = converter.getBufferedImage(frame);
        return bufferedImage;
    }

    /**
     * 是否是图片
     * 
     * @param ext
     * @return "jpg", "jpeg", "gif", "png", "bmp" 为文件后缀名者为图片
     */
    public static boolean getIsImg(String ext) {

        ext = ext.toLowerCase();
        for (String s : IMAGE_EXT) {
            if (s.equalsIgnoreCase(ext)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否是视频
     * 
     * @param ext
     * @return 支持格式: mp4, mpeg, 3gp, avi，宽高16:9或9:16，<500M
     */
    public static boolean getIsVideo(String ext) {

        ext = ext.toLowerCase();
        for (String s : VIDEO_EXT) {
            if (s.equalsIgnoreCase(ext)) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        // long size = 773752;
        // System.out.println(">>>>>>"+String.format("%.2f", size/1024/1024) + "M");
        /*String filename ="D:/dsdsd/dsdsd/sdasfsdasdfdfdasdfdfsa.mp4";
        //获取后缀名
        String suffix = filename.substring(filename.lastIndexOf("."));
        System.out.println(suffix);*/
        /*int a = 1536;
        double b = 0.2;
        int c = (int)(a * b);
        System.out.println(c);*/
		//fetchFrame("http://test.pangu.3399.com/upload/video/20210917/cb69ae58-aca6-4f57-a058-71a961e42026.mp4","E:/nginx-1.15.0/html/pig-ui/upload/video/test/aaaa.jpg");
		fetchFrameToInputStream("");
    }

}
